package com.swak.persistence.mybatis.methods;

import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.swak.define.ColumnDefine;
import com.swak.define.CurdDefine;
import com.swak.define.TableDefine;
import com.swak.utils.StringUtils;

/**
 * 处理通用的：method 注入
 * 
 * @author lifeng
 * @date 2021年1月21日 下午5:23:33
 */
public abstract class AbstractMethod implements CURDMethod, CurdDefine {
	protected static final Logger logger = LoggerFactory.getLogger(CURDMethod.class);
	protected Configuration configuration;
	protected LanguageDriver languageDriver;
	protected MapperBuilderAssistant builderAssistant;
	protected TableDefine<?> table;

	@Override
	public void inject(MapperBuilderAssistant builderAssistant, TableDefine<?> table) {
		this.configuration = builderAssistant.getConfiguration();
		this.builderAssistant = builderAssistant;
		this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
		this.table = table;
		this.doInject();
	}

	/**
	 * 子类实现注入
	 */
	protected abstract void doInject();

	/**
	 * 查询
	 */
	protected MappedStatement addSelectMappedStatement(String id, String sqlSource) {
		return addMappedStatement(id, sqlSource, SqlCommandType.SELECT, null, this.table.getResultMap(), null);
	}

	/**
	 * 查询
	 */
	protected MappedStatement addSelectMappedStatementForOther(String id, String sqlSource, Class<?> resultType) {
		return addMappedStatement(id, sqlSource, SqlCommandType.SELECT, null, null, resultType);
	}

	/**
	 * 插入
	 */
	protected MappedStatement addInsertMappedStatement(Class<?> parameterType, String id, String sqlSource) {
		return addMappedStatement(id, sqlSource, SqlCommandType.INSERT, parameterType, null, Integer.class);
	}

	/**
	 * 删除
	 */
	protected MappedStatement addDeleteMappedStatement(String id, String sqlSource) {
		return addMappedStatement(id, sqlSource, SqlCommandType.DELETE, null, null, Integer.class);
	}

	/**
	 * 更新
	 */
	protected MappedStatement addUpdateMappedStatement(String id, String sqlSource) {
		return addMappedStatement(id, sqlSource, SqlCommandType.UPDATE, null, null, Integer.class);
	}

	/**
	 * 更新
	 */
	protected MappedStatement addUpdateMappedStatement(Class<?> parameterType, String id, String sqlSource) {
		return addMappedStatement(id, sqlSource, SqlCommandType.UPDATE, parameterType, null, Integer.class);
	}

	/**
	 * 添加 MappedStatement 到 Mybatis 容器
	 */
	protected MappedStatement addMappedStatement(String id, String sql, SqlCommandType sqlCommandType,
			Class<?> parameterType, String resultMap, Class<?> resultType) {

		// ignore this Statement is exists
		String statementName = builderAssistant.applyCurrentNamespace(id, false);
		if (configuration.hasStatement(statementName, false)) {
			logger.warn(LEFT_SQ_BRACKET + statementName
					+ "] Has been loaded by XML or Mybatis's Annotation, so ignoring this injection for [" + getClass()
					+ RIGHT_SQ_BRACKET);
			return null;
		}

		boolean isSelect = false;
		if (sqlCommandType == SqlCommandType.SELECT) {
			isSelect = true;
		}

		if (logger.isDebugEnabled()) {
			logger.debug(StringUtils.format("Add Statement [%s] - [%s]", statementName, sql));
		}
		SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, this.table.entity);
		return builderAssistant.addMappedStatement(id, sqlSource, StatementType.PREPARED, sqlCommandType, null, null,
				null, parameterType, resultMap, resultType, null, !isSelect, isSelect, false, NoKeyGenerator.INSTANCE,
				null, null, configuration.getDatabaseId(), languageDriver, null);
	}

	/**
	 * 解析逻辑删除列： where 条件
	 */
	protected String parseWhereLogicDelEqualColumn(boolean value) {
		StringBuilder sql = new StringBuilder().append(WHERE);
		if (this.table.logicDelColumn != null) {
			sql.append(SPACE).append(this.parseLogicDelEqualColumn(false)).append(SPACE).append(AND);
		}
		return sql.append(SPACE).toString();
	}

	/**
	 * 解析逻辑删除列： where 条件
	 */
	protected String parseLogicDelEqualColumn(boolean value) {
		StringBuilder sql = new StringBuilder();
		if (this.table.logicDelColumn != null) {
			sql.append(this.table.logicDelColumn.name).append(EQUALS).append(value ? ONE : ZERO);
		}
		return sql.toString();
	}

	/**
	 * 解析列
	 */
	protected String parseSqlColumns() {
		StringBuilder sql = new StringBuilder();
		for (ColumnDefine column : table.columns) {
			String name = column.name;
			sql.append(name).append(SPLIT);
		}
		if (table.hasColumn()) {
			sql.delete(sql.lastIndexOf(SPLIT), sql.length() - 1);
		}
		return sql.toString();
	}

	/**
	 * 解析主键列, 可以多主键: ID = #{id} AND ID = #{id}
	 * 
	 * @param <T>
	 * @param table
	 * @return
	 */
	protected String parsePkEqualColumns() {
		StringBuilder sql = new StringBuilder();
		for (ColumnDefine column : table.getPkColumns()) {
			sql.append(column.name).append(EQUALS).append(HASH_LEFT_BRACE).append(column.javaProperty)
					.append(RIGHT_BRACE).append(AND);
		}
		if (table.hasColumn()) {
			sql.delete(sql.lastIndexOf(AND), sql.length() - 1);
		}
		return sql.toString();
	}

	/**
	 * 返回选择性的条件
	 * 
	 * @return
	 */
	protected String parseSelectiveWhereEqualColumns() {
		StringBuilder sql = new StringBuilder();
		sql.append(WHERE_START).append(NEWLINE);
		for (ColumnDefine column : table.getColumns()) {
			sql.append("<if test=\"").append(column.javaProperty).append(" != null\">");
			sql.append(AND).append(column.name).append(EQUALS).append(HASH_LEFT_BRACE).append(column.javaProperty)
					.append(RIGHT_BRACE);
			sql.append("</if>");
		}

		// 添加逻辑删除列
		if (this.table.logicDelColumn != null) {
			sql.append(AND).append(this.table.logicDelColumn.name).append(EQUALS).append(ZERO);
		}
		sql.append(NEWLINE).append(WHERE_END);
		return sql.toString();
	}

	/**
	 * 是否支持此 方法；
	 * 
	 * @param method
	 * @return
	 */
	protected boolean supportMethod(Sqls method) {
		return (method == Sqls.MERGE && this.table.define.useMerge()) // 支持 Merge
				|| (method == Sqls.LOCK && this.table.define.useLock())// 支持 Lock
				// || (method == Sqls.VERSION && this.table.define.useOptimismLock()) // 使用乐观锁
				|| (method.getScenes() != null && method.getScenes().contains(this.table.define.scene()));// 默认的支持
	}

	/**
	 * <p>
	 * 获取 带 if 标签的脚本
	 * </p>
	 *
	 * @param sqlScript sql 脚本片段
	 * @return if 脚本
	 */
	protected static String includeConditionClause() {
		return NEWLINE
				+ "<if test=\"valid\">(1 = 1 <foreach collection=\"criteria.criteriaWithoutValue\" item=\"item\" separator=\"AND\" open=\"AND\">${item}</foreach><foreach collection=\"criteria.criteriaWithSingleValue\" item=\"item\" separator=\"AND\" open=\"AND\">${item.condition} #{item.value}</foreach><foreach collection=\"criteria.criteriaWithListValue\" item=\"item\" separator=\"AND\" open=\"AND\">${item.condition}   <foreach collection=\"item.values\" item=\"value\" open=\"(\" close=\")\" separator=\",\">#{value}</foreach></foreach><foreach collection=\"criteria.criteriaWithBetweenValue\" item=\"item\" separator=\"AND\" open=\"AND\">  ${item.condition} #{item.first} AND #{item.second}</foreach><if test=\"orCriteriasValid\">AND (<foreach collection=\"orCriterias\" item=\"orCriteria\" separator=\"OR\" open=\"\">( 1 = 1 <foreach collection=\"orCriteria.criteriaWithoutValue\" item=\"item\" separator=\"AND\" open=\"AND\">${item}</foreach><foreach collection=\"orCriteria.criteriaWithSingleValue\" item=\"item\" separator=\"AND\" open=\"AND\">${item.condition} #{item.value}</foreach><foreach collection=\"orCriteria.criteriaWithListValue\" item=\"item\" separator=\"AND\" open=\"AND\">${item.condition}   <foreach collection=\"item.values\" item=\"value\" open=\"(\" close=\")\" separator=\",\">#{value}</foreach></foreach><foreach collection=\"orCriteria.criteriaWithBetweenValue\" item=\"item\" separator=\"AND\" open=\"AND\">  ${item.condition} #{item.first} AND #{item.second}</foreach>)</foreach>)</if>)</if>";
	}

	/**
	 * <p>
	 * 获取 带 if 标签的脚本
	 * </p>
	 *
	 * @param sqlScript sql 脚本片段
	 * @return if 脚本
	 */
	protected static String includeOrderClause() {
		return NEWLINE + "<if test=\"orderByClause!= null\">ORDER BY ${orderByClause}</if>";
	}

	/**
	 * <p>
	 * 获取 带 if 标签的脚本
	 * </p>
	 *
	 * @param sqlScript sql 脚本片段
	 * @return if 脚本
	 */
	protected static String includeClearSelectiveColumns() {
		return NEWLINE
				+ "<if test=\"columns != null\"><foreach collection=\"columns\" item=\"item\" separator=\",\" >${item} = NULL</foreach></if>";
	}

	/**
	 * <p>
	 * 获取 带 if 标签的脚本
	 * </p>
	 *
	 * @param sqlScript sql 脚本片段
	 * @return if 脚本
	 */
	protected static String includeSelectiveColumnClause() {
		return NEWLINE
				+ "<if test=\"columns != null\"><foreach collection=\"columns\" item=\"item\" separator=\",\" >${item}</foreach></if>";
	}
}